//2009-12-30

#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMouseEvent>
#include <QPointF>
#include <QSlider>
#include <cmath>
#include <gsl/gsl_randist.h>
#include "simulator.h"
#include "hotspot.h"

MapBlock *mapblock[SIZE][SIZE];
extern BaseStation *BS;
extern Hotspot hotspot[MAXHOTSPOT];
extern int *OpenBS;

// Color policy for coverage
QColor BSCoverage[3]={
		QColor(0,255,0,40),
		QColor(128,255,0,40),
		QColor(128,0,255,40)
};
QColor BSLink[3] = {
		QColor(Qt::yellow),
		QColor(Qt::red),
		QColor(Qt::darkMagenta)
};

// Global random variable
const gsl_rng_type * T;
gsl_rng *r;

// For mouse zooming
void
CityMap::wheelEvent( QWheelEvent *event )
{
	int numSteps = event->delta() / 120;
	int delta_scale = (m_scale + numSteps > MAXZOOM)? MAXZOOM - m_scale: numSteps;
	delta_scale = (m_scale + numSteps < MINZOOM ) ? MINZOOM-m_scale: delta_scale;

	if (delta_scale != 0)
	{
		Rescale(m_scale + delta_scale);
	}
}

// Map Zooming
void
CityMap::Rescale (int new_scale)
{
	int delta_scale = new_scale - m_scale;
	m_scale = new_scale;
	double factor = pow( 1.125 , delta_scale );
	scale(factor, factor);
	emit(valueChanged(m_scale));
}

// Handle mouse press event: left button to move the map view
//                           right button to select center
void
CityMap::mousePressEvent( QMouseEvent *event )
{
	if(event->button() == Qt::LeftButton)
	{
		time.start();
		s1.setX(event->x());
		s1.setY(event->y());
	}
	else if (event->button() == Qt::RightButton)
	{
		QPointF	s = mapToScene(event->x(),event->y());
		if ((s.x() < SIZE*20 && s.y()< SIZE*20)
		 && (s.x() >=0  && s.y()>= 0))
		{
			emit SetHotspotCenter(int(s.x()/20),int(s.y()/20));
		}
	}
}

// Handle mouse drag event
void
CityMap::mouseReleaseEvent( QMouseEvent *event )
{
	if(event->button() == Qt::LeftButton)
	{
		if (time.elapsed() > 250)
		{
			QPoint s0(size().width()/2, size().height()/2);
			s2.setX(event->x());
			s2.setY(event->y());
			QPoint sn = (s1 -s2)/2 + s0 ;
			QPointF s = mapToScene(sn.x(),sn.y());
			centerOn(s.x(),s.y());
		} else
		{
			QPointF s = mapToScene(event->x(),event->y());
		}
	}
}

// Handle mouse move display
void
CityMap::mouseMoveEvent( QMouseEvent *event )
{
	QPointF	s = mapToScene(event->x(),event->y());
	int X = int (s.x() / 20),Y = int (s.y()/20);
	arrow->hide();
	if ((X < SIZE && Y < SIZE)
	 && (X >=0  && Y>= 0))
	{
		emit Show(X,Y);
		RFUnit* serveUnit = mapblock[X][Y]->GetServeUnit();
		int	serveChannel = mapblock[X][Y]->GetServeChannel();
		if (serveUnit)
		{
			int BSX = serveUnit->GetX(),
				BSY = serveUnit->GetY();
			arrow->setPen(QPen(BSLink[serveChannel % 3], 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
			arrow->setLine(10 + BSX*20,10+ BSY*20,s.x(),s.y());
			arrow->show();
		}
	}

}

// Generate city map
CityMap::CityMap( QWidget *parent)
{
	Scene = new QGraphicsScene( 0 , 0 , 20 * SIZE , 20 * SIZE );
	Scene->setBackgroundBrush( QColor( 255 , 255 , 238 ) );

	GenerateCityBlock();

	arrow = new QGraphicsLineItem;
	arrow->setZValue(1);
	arrow->setPen(QPen(Qt::darkBlue, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
	Scene->addItem(arrow);

	numHotspot = 2;
	hotspot[0].Set(SIZE/3,  SIZE/3*2,12,12,20000);
	hotspot[1].Set(int(SIZE*0.8),SIZE/2,   8, 8,40000);
	GenerateTraffic();

	setScene(Scene);
	setDragMode(ScrollHandDrag);
	centerOn(1500,1000);
	m_scale = 1;
	setMouseTracking(true);

}

// Update specific block painting
void
CityMap::RepaintBlock( int i , int j )
{
	Scene->update( 20 * i , 20 * j , 20 , 20 );
}

// Update whole map
void
CityMap::RepaintAll()
{
	Scene->update( 0 , 0 , 20*SIZE , 20*SIZE );
}

void
CityMap::GenerateCityBlock()
{
	int i , j;
	for( i = 0 ; i < SIZE ; i++ )
	{
		for( j = 0 ; j < SIZE ; j++ )
		{
			mapblock[i][j] = new MapBlock(i,j);
			Scene->addItem( mapblock[i][j] );
		}
	}
}

// Destructor
CityMap::~CityMap()
{
	for( int i = 0 ; i < SIZE ; i++ )
	{
		for( int j = 0 ; j < SIZE ; j++ )
		{
			delete mapblock[i][j];
		}
	}
}

enum TrafficPattern Simulator::pattern = Day;

MapBlock::MapBlock( int X, int Y )
:x(X),y(Y)
{
	/*powerBS = new double[BaseStation::BSNumber];
	for(int i = 0; i < BaseStation::BSNumber; i++)
	{
		powerBS[i] = 0.0;
	}*/
	BSExist = false;
	userCovered = false;
	serveUnit = NULL;
	serveChannel = -1;

	setFlags( ItemIsSelectable );
	setZValue(0);
}

void
MapBlock::SetBSExist( bool flag )
{
	BSExist = flag;
}

bool
MapBlock::GetBSExist()
{
	return BSExist;
}

int
MapBlock::GetX() const
{
	return x;
}

int
MapBlock::GetY() const
{
	return y;
}

bool
MapBlock::GetUserCovered() const
{
	return userCovered;
}

void
MapBlock::SetUserCovered( bool flag )
{
	userCovered = flag;
}


int
MapBlock::GetUserNumber() const
{
	return userNumber;
}

void
MapBlock::SetUserNumber(int user)
{
	userNumber = user;
	CalculateTraffic();
}

double
MapBlock::GetTotalDayTraffic() const
{
	return totalDayTraffic;
}

double
MapBlock::GetTotalNightTraffic() const
{
	return totalNightTraffic;
}

void
MapBlock::SetServeUnit(RFUnit* unit)
{
	serveUnit = unit;
}

RFUnit*
MapBlock::GetServeUnit() const
{
	return serveUnit;
}

void
MapBlock::SetServeChannel(int chan)
{
	serveChannel = chan;
}

int
MapBlock::GetServeChannel() const
{
	return serveChannel;
}

// Block position and geometry
QRectF
MapBlock::boundingRect() const
{
	return QRectF( 20 * x , 20 * y , 20 ,20 );
}

// Block coloring
void
MapBlock::paint( QPainter* painter , const QStyleOptionGraphicsItem* option , QWidget* widget )
{
	QPen pen( Qt::white , 0, Qt::DotLine );
	painter->setPen(pen);

	QColor color = QColor(0,128,255,50);
	int level1 = COMMONDENSITY, level2 = COMMONDENSITY + 25, level3 = COMMONDENSITY + 60;
	if (userNumber < level1)
	{
	}
	else if (userNumber < level2)
	{
		double alpha = (userNumber - level1) / ( level2 - level1 + .0);
		color = QColor(int(255*alpha),int(128+(255-128)*alpha),
				       int(255+(128-255)*alpha),int(50+50*alpha));
	}
	else if (userNumber < level3)
	{
		double alpha = (userNumber - level2) / ( level3 - level2 + .0) ;
		color = QColor(255,int(128-128*alpha),int(128-128*alpha),int(100+30*alpha));
	}
	else
	{
		color = QColor(255,0,0,130);
	}

	if( BSExist )
	{
		painter->setBrush( QBrush( Qt::white , Qt::SolidPattern ) );
		for(int i = 0; i < BaseStation::BSNumber; i++ )
		{
			if( BS[i].GetX() == x && BS[i].GetY() ==y && BS[i].IsOpen() )
			{
				painter->setBrush( QBrush( BSCoverage[serveChannel % 3] , Qt::SolidPattern ) );
			}
		}
		painter->drawRect( QRect( 20 * x , 20 * y , 20 ,20 ) );
		painter->setBrush(QBrush(Qt::black, Qt::SolidPattern));
		painter->drawRoundRect( QRect( 5 +20 * x , 5 + 20 * y , 10 , 10 ) , 80 , 80 );
	}
	else
	{
		if( Simulator::IsDay() )
		{
			if( userCovered && CityMap::layer == CityMap::COVERAGE)
			{
				painter->setBrush( BSCoverage[serveChannel % 3] );
			}
			else
			{
				painter->setBrush( color );
			}
		}
		else
		{
			if( userCovered && CityMap::layer == CityMap::COVERAGE)
			{
				painter->setBrush( Qt::green );
			}
			else
			{
				painter->setBrush( color );
			}
		}
		painter->drawRect( QRect( 20 * x , 20 * y , 20 ,20 ) );
		painter->drawRect( boundingRect() );
		painter->setBrush( QBrush( Qt::black , Qt::SolidPattern ) );
	}
}

enum CityMap::DisplayLayer CityMap::layer = CityMap::TRAFFIC;

// GUI initialization
Simulator::Simulator()
{

	// Setup Random Number Generator
	QTime tmp = QTime::currentTime();
	gsl_rng_env_setup();
	T=gsl_rng_default;
	r = gsl_rng_alloc (T);
	tmp = QTime::currentTime();
	gsl_rng_set(r, tmp.msec()+tmp.second()*1000);

	// Initial GUI
	CityMap::layer = CityMap::TRAFFIC;

	InputBSSites();

	SetTrafficLabel = new QLabel( tr( "Set traffic pattern" ) );
	SetTraffic = new QComboBox;
	SetTraffic->addItem("Day");
	SetTraffic->addItem("Night");
	SetTraffic->addItem("More");
	pattern = Day;
	connect( SetTraffic, SIGNAL(currentIndexChanged(int)), this, SLOT(SetTrafficPattern(int)));

	//DEBUG!!!
	SimuStep = new QSpinBox;
	SimuStep->setRange(1,10);

	SetHotSpots = new QLabel( tr( "Hot Spots Number:" ) );
	HotSpots = new QSpinBox;
	HotSpots->setRange(0, MAXHOTSPOT);
	HotSpots->setValue(2);

	SetHotNumber = new QPushButton( tr( "Set" ) );
	SetHotNumber->setEnabled( true );
	connect( HotSpots , SIGNAL( valueChanged( int ) ) , this , SLOT( InputHotSpots( int ) ) );
	connect( SetHotNumber , SIGNAL( clicked() ) , this , SLOT( SetHotspot() ) );

	ShowCoverRate = new QLabel( tr( "Cover Rate:" ) );
	CoverRate = new QLineEdit;
	ShowTotalUserNumber = new QLabel( tr( "Total  User Number:" ) );
	TotalUserNumber = new QLineEdit;
	ShowServedUserNumber = new QLabel( tr( "Served User Number:" ) );
	ServedUserNumber = new QLineEdit;

	StartSimulation = new QPushButton( tr( "Start Simulation" ) );
	connect( StartSimulation , SIGNAL( clicked() ) , this , SLOT( StartBSSimulation() ) );

	Reset = new QPushButton( tr( "Reset to  Resimulation" ) );
	Reset->setEnabled( false );
	connect( Reset , SIGNAL( clicked() ) , this , SLOT( ResetBSSimulation() ) );

	Layer = new QPushButton( tr( "Coverage" ) );
	Layer->setCheckable(true );
	connect( Layer, SIGNAL( toggled(bool) ), this, SLOT( DisplayLayer(bool) ));

	QHBoxLayout* right1 = new QHBoxLayout;
	right1->addWidget( SetTrafficLabel );
	right1->addWidget( SetTraffic );

	QHBoxLayout* right5 = new QHBoxLayout;
	right5->addWidget( SetHotSpots );
	right5->addWidget( HotSpots );
	right5->addWidget( SetHotNumber );

	QHBoxLayout* right2 = new QHBoxLayout;
	right2->addWidget( ShowCoverRate );
	right2->addWidget( CoverRate );

	QHBoxLayout* right3 = new QHBoxLayout;
	right3->addWidget( ShowTotalUserNumber );
	right3->addWidget( TotalUserNumber );

	QHBoxLayout* right4 = new QHBoxLayout;
	right4->addWidget( ShowServedUserNumber );
	right4->addWidget( ServedUserNumber );

	QVBoxLayout* right = new QVBoxLayout;
	right->addWidget(SimuStep);//DEBUG!!!
	right->addLayout( right1 );
	right->addLayout( right5 );

	for (int i = 0; i< MAXHOTSPOT; i++)
	{
		hotspot_config[i] = new Hotspot_Config(i);
		connect(citymap, SIGNAL(SetHotspotCenter(int,int)),
				hotspot_config[i], SLOT(SetCenter(int,int)));
		right->addWidget(hotspot_config[i]);
	}

	right->addStretch();
	right->addWidget( StartSimulation );
	right->addWidget( Reset );
	right->addWidget( Layer );
	right->addStretch();
	right->addLayout( right2 );
	right->addLayout( right3 );
	right->addLayout( right4 );
	right->addStretch();
	
	labelMousePos = new QLabel();
	labelMousePos->setText("Position:(0,0)");
	connect(citymap, SIGNAL( Show(int,int) ), this, SLOT( DisplayPosition(int,int) ));
	right->addWidget(labelMousePos);

	QSlider *zoomSlider = new QSlider(Qt::Vertical);
	zoomSlider->setRange(MINZOOM,MAXZOOM);
	connect(zoomSlider, SIGNAL(valueChanged(int)), citymap, SLOT(Rescale(int)));
	connect(citymap,SIGNAL(valueChanged(int)),zoomSlider,SLOT(setValue(int)));

	QHBoxLayout* mainlayout = new QHBoxLayout;
	mainlayout->addWidget( citymap );
	mainlayout->addWidget( zoomSlider );
	mainlayout->addLayout( right );

	setLayout( mainlayout );
	setWindowTitle( tr( "Network Planning" ) );

}

Simulator::~Simulator()
{
	gsl_rng_free(r);
}

bool
Simulator::IsDay()
{
	return (pattern == Day);
}

void
Simulator::SetTrafficPattern( enum TrafficPattern ptn )
{
	pattern = ptn;
}

void
Simulator::SetTrafficPattern( int index )
{
	switch(index)
	{
	case 0:	SetTrafficPattern(Day);break;
	case 1:	SetTrafficPattern(Night);break;
	case 2:	SetTrafficPattern(More);break;
	default:SetTrafficPattern(Day);break;
	}
}

// Display block info
void
Simulator::DisplayPosition(int x, int y)
{
	QString tmp = QString::number(mapblock[x][y]->GetUserNumber())
	           + "(" + QString::number(x) +  "," + QString::number(y) +")\n";
	double intall[3];
	for (int k = 0; k < CHANNEL; k++)
	{
		double pmax = -1000, imax = -1;
		intall[k] = 0.0;
		for (int i = 0; i < BaseStation::BSNumber; i++)
		{
			if (BS[i].IsOpen())
			{
				if (pmax < BS[i].CalculatePower(x,y, k ) )
				{
					imax = i;
					pmax = BS[i].CalculatePower(x,y, k );
				}
				intall[k] += pow(10, BS[i].CalculatePower(x,y, k) / 10.0);
			}
		}
		tmp += QString::number(pow(10,pmax/10.0)) + "(" + QString::number(imax)+")["
			   + QString::number(k)+ "]," +QString::number(intall[k]) + "\n";
	}
	labelMousePos->setText(tmp);
	if( mapblock[x][y]->GetServeUnit() )
	{
		RFUnit* serveUnit = mapblock[x][y]->GetServeUnit();
		int	chan = mapblock[x][y]->GetServeChannel();
		double servePower = pow(10, serveUnit->CalculatePower(x,y, chan) / 10.0);
		if ( dynamic_cast<BaseStation*>(serveUnit) )
		{
			BaseStation* serveBS = dynamic_cast<BaseStation*>(serveUnit);
			labelMousePos->setText(labelMousePos->text()+ "\nServe BS:"
								   + QString::number(serveBS->GetNumber())
								   + "[" + QString::number(chan) + "]"
								   + "(" + QString::number(serveBS->GetX()) + ","
								   + QString::number(serveBS->GetY()) + ")\n         "
								   + QString::number(servePower / (intall[chan] - servePower))+"\n         "
								   + QString::number(serveBS->GetBand(chan)));
		}
	}
}

void
Simulator::DisplayLayer(bool checked)
{
	if (!checked)
	{
		Layer->setText("Coverage");
		CityMap::layer = CityMap::TRAFFIC;
		citymap->RepaintAll();
	}
	else
	{
		Layer->setText("Traffic");
		CityMap::layer = CityMap::COVERAGE;
		citymap->RepaintAll();
	}

}
